#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include <time.h>

#define EPSILON    0.1
#define THREAD_MAX 20

struct Params {
	//in
	double left;
	double right;
	double fleft;
	double fright;
	double lrarea;
	//out
	double result;
};

sem_t semaphore;
long threadCount;

double f(double p);

double quad(double left, double right);
void * quadR(void *);

int main() {
	sem_init(&semaphore, 0, 1);
	clock_t v_time;
	double result, left = 0, right;

	for(int i = 2; i < 10; ++i) {
		//printf("\nbeg start: %lu \n", v_time);
		right = pow(10.0, i);
		v_time  = clock();
		result = quad(left, right);
		v_time = clock() - v_time;

		printf("left: %.1f | right: %.1f \t result: %.1f \t time: %f s\n\n", left, 
																			right, 
																			result, 
																			((float)v_time)/CLOCKS_PER_SEC);
		//printf("end start: %lu \n\n", clock());
	}

	sem_destroy(&semaphore);
	return 0;
}

double f(double x) {
	double pi = 0;
	float d = 1;
	for(long i = 0; i < 1000000; ++i) {
		pi = pi + (i % 2 == 0? 1 : -1) / d;
		d += 2;
	}
	pi *= 4;
	pi /= M_PI;
	return pi * sqrt(x);
}

double quad(double left, double right) {
	Params p;
	p.left = left;
	p.right = right;
	p.fleft = f(p.left);
	p.fright = f(p.right);
	p.lrarea = (p.fleft + p.fright) * (p.right - p.left) / 2;

	pthread_t startThread;
	pthread_create(&startThread, NULL, quadR, (void*)&p);
	pthread_join(startThread, NULL);

	return p.result;
}

void * quadR(void * paramPtr) {
	Params &p = *((Params *)paramPtr);
	
	double mid = (p.left + p.right) / 2;
	double fmid = f(mid);
	double larea = (p.fleft + fmid) * (mid - p.left) / 2;
	double rarea = (fmid + p.fright) * (p.right - mid) / 2;
	
	bool isThreadCreate = 0;
	double _val = (larea + rarea) - p.lrarea;
	
	if((_val < 0? -1 : 1) * _val > EPSILON) {
		Params paramsLeftPtr;
		paramsLeftPtr.left   = p.left;
		paramsLeftPtr.right  = mid;
		paramsLeftPtr.fleft  = p.fleft;
		paramsLeftPtr.fright = fmid;
		paramsLeftPtr.lrarea = larea;

		Params paramsRightPtr;
		paramsRightPtr.left   = mid;
		paramsRightPtr.right  = p.right;
		paramsRightPtr.fleft  = fmid;
		paramsRightPtr.fright = p.fright;
		paramsRightPtr.lrarea = rarea;

		if(true) { /*threadCount <= THREAD_MAX*/ /*threadCount >= THREAD_MAX*/
			pthread_t threads[2];

			sem_wait(&semaphore);
			threadCount += 2;
			sem_post(&semaphore);
			
			pthread_create(&threads[0], NULL, quadR, &paramsLeftPtr);
			pthread_create(&threads[1], NULL, quadR, &paramsRightPtr);

			pthread_join(threads[0], NULL);
			pthread_join(threads[1], NULL);

			sem_wait(&semaphore);
			threadCount -= 2;
			sem_post(&semaphore);
			isThreadCreate = 1;
		} else {
			quadR(&paramsLeftPtr);
			quadR(&paramsRightPtr);
		}
		
		if(paramsLeftPtr.result >= 0) {
			larea = paramsLeftPtr.result;
			rarea = paramsRightPtr.result;
		}
	}

	p.result = larea + rarea;

	if(isThreadCreate) {
		pthread_exit(0);
	}
	return 0;
}
